﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Mime;
using System.Text;
using System.Web;

namespace Super.Common
{
    public static class Log
    {
        static Log()
        {
            FileLog = new FileLog();
        }

        public static FileLog FileLog { get; private set; }

        /// <summary> 获取IP 
        /// </summary>
        /// <returns></returns>
        public static string GetIp()
        {
            string ip;
            if (HttpContext.Current.Request.ServerVariables["HTTP_VIA"] != null && !String.IsNullOrEmpty(HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"])) //使用了代理并且有透传IP
                ip = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
            else
                ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];

            return ip.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries).Any(j => { int k; return !Int32.TryParse(j, out k); }) ? HttpContext.Current.Request.UserHostAddress : ip;
        }

        public static Func<string> GetUserName;
    }

    /// <summary> 日志类别.
    /// </summary>
    public enum LogType
    {
        /// <summary> 提示 (提示信息, 无关紧要)
        /// </summary>
        Hint = 1,

        /// <summary> 信息 (需要记录的一些重要的信息 )
        /// </summary>
        Info,

        /// <summary> 错误 (程序发生错误.)
        /// </summary>
        Error,

        /// <summary> 未处理的异常 (由Filter捕获的异常..代码中未处理到的异常.很严重)
        /// </summary>
        Exception,

        /// <summary> 非常严重的错误.(可直接导致数据或系统崩溃的错误. 非常严重.)
        /// </summary>
        VerySeriousError
    }

    /// <summary> 日志信息
    /// </summary>
    public class LogInfo
    {

        /// <summary> 行号
        /// </summary>
        public int Line;

        /// <summary> 信息
        /// </summary>
        public string Info = "";

        /// <summary>
        /// 类别
        /// </summary>
        public LogType Type;

        /// <summary>
        /// URL
        /// </summary>
        public string Url = "";

        /// <summary> 来路Url
        /// </summary>
        public string RefUrl = "";

        /// <summary> 用户 
        /// </summary>
        public string UserName = "";

        /// <summary>
        /// 客户端IP
        /// </summary>
        public string Ip = "";

        /// <summary>
        /// 创建时间.
        /// </summary>
        public DateTime DateTime;

        /// <summary> 日志明细.
        /// </summary>
        public string Detail = "";


        /// <summary> 从字符串生成Info
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static LogInfo FormString(string s)
        {
            var li = new LogInfo();
            int inx;
            if ((inx = s.IndexOf("<DateTime:")) >= 0)
            {
                inx += 10;
                li.DateTime = new DateTime(long.Parse(s.Substring(inx, s.IndexOf(">", inx, StringComparison.Ordinal) - inx)));
            }

            if ((inx = s.IndexOf("<Type:")) >= 0)
            {
                inx += 6;
                li.Type = (LogType)Enum.Parse(typeof(LogType), s.Substring(inx, s.IndexOf(">", inx, StringComparison.Ordinal) - inx));
            }

            if ((inx = s.IndexOf("<URL:")) >= 0)
            {
                inx += 5;
                li.Url = s.Substring(inx, s.IndexOf(">", inx, StringComparison.Ordinal) - inx);
            }

            if ((inx = s.IndexOf("<RefURL:")) >= 0)
            {
                inx += 8;
                li.RefUrl = s.Substring(inx, s.IndexOf(">", inx, StringComparison.Ordinal) - inx);
            }

            if ((inx = s.IndexOf("<IP:")) >= 0)
            {
                inx += 4;
                li.Ip = s.Substring(inx, s.IndexOf(">", inx, StringComparison.Ordinal) - inx);
            }

            if ((inx = s.IndexOf("<Info:")) >= 0)
            {
                inx += 6;
                li.Info = s.Substring(inx, s.IndexOf(">", inx, StringComparison.Ordinal) - inx).Replace("{yj}", ">").Replace("{brhh}", "\r\n");
            }

            if ((inx = s.IndexOf("<UserName:")) >= 0)
            {
                inx += 10;
                li.UserName = s.Substring(inx, s.IndexOf(">", inx, StringComparison.Ordinal) - inx);
            }

            if ((inx = s.IndexOf("<Detail:")) >= 0)
            {
                inx += 8;
                li.Detail = s.Substring(inx, s.IndexOf(">", inx, StringComparison.Ordinal) - inx).Replace("{yj}", ">").Replace("{brhh}", "\r\n");
            }

            return li;
        }

        /// <summary> 从信息创建LogInfo
        /// </summary>
        /// <param name="info"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static LogInfo FormInfo(string info, LogType type)
        {
            var li = new LogInfo();

            try
            {
                li.Info = info;
                li.Type = type;
                li.DateTime = DateTime.Now;
                li.Ip = Log.GetIp();
                li.UserName = Log.GetUserName();
                li.Url = HttpContext.Current.Request.Url.PathAndQuery;
                li.RefUrl = HttpContext.Current.Request.UrlReferrer != null ? HttpContext.Current.Request.UrlReferrer.AbsoluteUri : "";
            }
            catch (Exception)
            { }

            return li;
        }

        /// <summary> 转换为字符串.
        /// </summary>
        /// <returns></returns>
        new public string ToString()
        {
            return "<DateTime:" + DateTime.Ticks + "><Type:" + Type + "><Detail:" + Detail.Replace(">", "{yj}").Replace("\r\n", "{brhh}") + "><URL:" + Url + "><RefURL:" + RefUrl + "><IP:" + Ip + "><Info:" + Info.Replace(">", "{yj}").Replace("\r\n", "{brhh}") + "><UserName:" + UserName + ">";
        }
    }

    /// <summary> 文件日志
    /// </summary>
    public class FileLog
    {
        public int NewLogCount { get; set; }

        public int NewLogWriteError { get; set; }

        private static FileInfo _fileInfo = new FileInfo(HttpContext.Current.Server.MapPath("~/Log.ini"));


        public FileLog()
        {
            lock (_fileInfo)
            {
                if (!_fileInfo.Exists)
                {
                    _fileInfo.Create().Close();
                }
            }
        }

        /// <summary> 写入日志. info中不需包含时间,IP,URL等信息
        /// </summary>
        /// <param name="info"></param>
        /// <param name="type"></param>
        /// <param name="detail">异常明细 </param> 
        public void Write(string info, LogType type, string detail = "")
        {
            try
            {
                var li = LogInfo.FormInfo(info, type);
                li.Detail = detail;

                Write(li.ToString());

                NewLogCount++;
            }
            catch
            {
                NewLogWriteError++;
            }
        }

        /// <summary> 写入日志.
        /// </summary>
        /// <param name="content"></param>
        private static void Write(string content)
        {
            var bs = Encoding.UTF8.GetBytes(content + "\r\n");
            lock (_fileInfo)
            {
                var fs = _fileInfo.OpenWrite();
                try
                {
                    fs.Seek(0, SeekOrigin.End);
                    fs.Write(bs, 0, bs.Length);
                }
                finally
                {
                    fs.Close();
                }
            }
        }

        /// <summary> 读取日志信息
        /// </summary>
        /// <param name="count"> </param> 
        /// <param name="userName"> </param>
        /// <param name="info"></param>
        /// <param name="logType">显示的最小类别等级.(显示最小类别以上的错误.)</param>
        /// <param name="ip"></param>
        /// <param name="beginTime"></param>
        /// <param name="endTime"></param>
        /// <param name="url"></param>
        /// <returns></returns>
        public List<LogInfo> Read(int count = 200, string userName = null, string info = null, LogType? logType = null, string ip = null, DateTime? beginTime = null, DateTime? endTime = null, string url = null)
        {
            var lli = new List<LogInfo>();

            var line = 1;

            lock (_fileInfo)
            {
                var fs = _fileInfo.OpenRead();
                try
                {
                    var sr = new StreamReader(fs);

                    while (!sr.EndOfStream)
                    {
                        var rl = sr.ReadLine();
                        var li = LogInfo.FormString(rl);
                        li.Line = line++;
                        lli.Add(li);
                    }

                    sr.Close();
                }
                finally
                {
                    fs.Close();
                }
            }

            return lli.Where
                (j =>
                       (info == null || j.Info.Contains(info))
                    && (logType == null || (int)j.Type >= (int)logType.Value)
                    && (beginTime == null || j.DateTime > beginTime)
                    && (endTime == null || j.DateTime < endTime)
                    && (url == null || j.Url.Contains(url))
                    && (userName == null || j.UserName.ToUpper().Contains(userName.ToUpper()))
                    && (ip == null || j.Ip.Contains(ip))
                )
                .OrderByDescending(j => j.DateTime)
                .Take(count).ToList();
        }

        public LogInfo ReadByLine(int line)
        {

            lock (_fileInfo)
            {
                var fs = _fileInfo.OpenRead();
                try
                {
                    var sr = new StreamReader(fs);

                    string s = "";

                    for (int i = 0; i < line; i++)
                    {
                        s = sr.ReadLine();
                    }

                    var li = LogInfo.FormString(s);

                    return li;
                }
                finally
                {
                    fs.Close();
                }

            }
        }

        public string Clear()
        {
            lock (_fileInfo)
            {
                _fileInfo.Delete();
                _fileInfo.Create().Close();
                return "ok";
            }
        }
    }

}
